﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Timers;
using log4net;

namespace LeakView.common
{
    public interface IModule
    {

        void OnModuleInitialize();

        void OnModuleTerminated();

    }

    public abstract class TimerModule : IModule
    {
        //private Timer timer = null;
        private static readonly ILog log = LogManager.GetLogger(typeof(TimerModule));

        private System.Timers.Timer timer = null;

        private int period = 1000;

        public int Period
        {
            get { return period; }
            set
            {
                if (this.period != value)
                {
                    this.period = value;

                }
            }
        }

        public virtual void OnModuleInitialize()
        {
            timer = new System.Timers.Timer
            {
                Interval = Period,
                AutoReset = false
            };
            timer.Elapsed += new ElapsedEventHandler(this.DispatchTimer);
            timer.Start();
        }

        private void DispatchTimer(object sender, ElapsedEventArgs e)
        {
            this.OnTimer();

            System.Timers.Timer tm = this.timer;

            if (tm != null)
            {
                //log.Info("Timer state: " + tm.Enabled);

                if (!tm.Enabled)
                {
                    tm.Interval = Period;
                    tm.Enabled = true;
                }
            }


        }

        public virtual void OnModuleTerminated()
        {
            if(this.timer != null)
            {
                this.timer.Enabled = false;
                this.timer.Dispose();
                this.timer = null;
            }

        }

        protected abstract void OnTimer();
    }

    public abstract class ThreadModule : IModule
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ThreadModule));

        private Thread thread = null;
        
        private int period = 1000;

        public int Period
        {
            get { return period; }
            set
            {
                if(this.period != value)
                {
                    this.period = value;
                }
            }
        }

        public virtual void OnModuleInitialize()
        {
            this.thread = new Thread(() => ThreadHandler());
            this.thread.Start();
        }

        public virtual void OnModuleTerminated()
        {
            if(this.thread != null)
            {
                try
                {
                    Thread old = this.thread;
                    this.thread = null;
                    old.Join();
                    
                }
                catch
                {

                }
            }
        }

        private void ThreadHandler()
        {
            log.Debug("TimerModule[" + this.GetType().ToString() + "] started.");

            while (thread != null)
            {
                try
                {
                    OnTimer();
                }
                catch(Exception e)
                {
                    log.Warn("Exception on timer handler", e);
                }
                
                if(thread != null)
                {
                    Thread.Sleep(Period);
                }
            }

            log.Debug("TimerModule[" + this.GetType().ToString() + "] end.");
        }

        protected abstract void OnTimer();
    }

    public class ModuleManager : IDisposable
    {
        private List<IModule> modules = new List<IModule>();

        public void Register(IModule module)
        {
            modules.Add(module);
        }

        public void Start()
        {
            modules.ForEach((m) =>
            {
                m.OnModuleInitialize();
            });

        }

        public void Dispose()
        {
            Stop();
        }

        public void Stop()
        {
            List<IModule> temp = new List<IModule>();
            temp.AddRange(modules);
            temp.Reverse();
            temp.ForEach((m) =>
            {
                m.OnModuleTerminated();
            });
        }

    }
}
